<?php
// 
// Tar class
// Unpacks tar files (optional .gz compression)
// 
// (c) Steve H 2008 :: GPL v3
// 
class tar {
    // Initial variables
    var $file = false;
    var $filename = false;
    var $pointer = 0;
    var $marks = array();
    var $curdata = false;
    var $filelist = array();
    var $traversed = false;
    
    
    function tar($file=false,$gz=false) {
        if ($file) $this->load($file,$gz);
    }
    
    // Load tar file
    function load($file,$gz=false) {
        $this->close();
        
        if ( ($gz) && (!function_exists('gzopen')) ) {
            trigger_error('gz compression not supported by this PHP installation',E_USER_ERROR);
            return false;
        }
        if (!$this->file = ($gz?gzopen($file,'rb'):fopen($file,'rb'))) {
            trigger_error('tar file failed to load',E_USER_ERROR);
            return false;
        }
        $this->filename = $file;
        $this->reset();
        return true;
    }
    
    // Close tar file
    function close() {
        if ($this->file) {
            $this->reset();
            fclose($this->file);
        }
        $this->file = false;
        $this->filename = false;
        $this->marks = array();
        $this->filelist = array();
        $this->traversed = false;
        return true;
    }
    
    
    
    // Reset internal file pointer
    function reset() {
        if (!$this->file) return $this->nofile();
        rewind($this->file);
        $this->pointer = 0;
        $this->curdata = false;
        return $this->loadcur();
    }
    
    
    
    // Set pointer to previous file
    function prev() {
        if (!$this->file) return $this->nofile();
        
        if ($this->pointer == 0) return false;
        $this->pointer--;
        
        fseek($this->file,$this->marks[$this->pointer]);
        return $this->loadcur();
    }
    // Set pointer to next file
    function next() {
        if (!$this->file) return $this->nofile();
        
        $this->pointer++;
        
        return $this->loadcur();
    }
    // Goto specified file
    function seek($i) {
        if ($this->pointer == $i) return true;
        if ($this->pointer > $i) {
            fseek($this->file,$this->marks[$i]);
            return $this->loadcur();
        }
        while ( ($r = $this->next()) && ($this->pointer < $i) );
        return $r;
    }
    // Load selected file
    function loadcur() {
        $pointer = ftell($this->file);
        
        $headers = fread($this->file,512);
        if ($headers == str_repeat(chr(0),512)) {
            $this->traversed = true;
            return false;
        }
        
        $this->curdata = array(
            'name'=>str_replace(chr(0),'',substr($headers,0,100)),
            'mode'=>octdec(substr($headers,100,8)),
            'uid'=>octdec(substr($headers,108,8)),
            'gid'=>octdec(substr($headers,116,8)),
            'size'=>octdec(substr($headers,124,12)),
            'time'=>octdec(substr($headers,136,12)),
            'checksum'=>octdec(substr($headers,148,6)),
            'type'=>octdec(substr($headers,156,1)),
        );
        
        if (substr($headers,257,5) == 'ustar') {
            $this->curdata['name'] = str_replace(chr(0),'',substr($headers,345,155)).$this->curdata['name'];
        }
        
        if ($this->checksum($headers) != $this->curdata['checksum']) {
            trigger_error('Tar checksum failed',E_USER_ERROR);
            return false;
        }
        
        $this->marks[$this->pointer] = $pointer;
        $this->filelist[$this->pointer] = $this->curdata['name'];
        
        fseek($this->file,($pointer+512+(ceil($this->curdata['size']/512)*512)));
        
        return true;
    }
    
    
    
    function checksum($str) {
        $sum = 256;
        for ($n=0;$n<512;$n++) if (($n<148)||($n>155)) $sum += ord($str[$n]);
        return $sum;
    }
    
    
    
    // Return file data
    function data($data=false) {
        if (!$this->file) return $this->nofile();
        return $this->curdata;
    }
    // Return file contents
    function curfile() {
        if (!$this->file) return $this->nofile();
        if ($this->curdata['size']) {
            fseek($this->file,($this->marks[$this->pointer]+512));
            $data = fread($this->file,$this->curdata['size']);
            fseek($this->file,($this->marks[$this->pointer]+512+(ceil($this->curdata['size']/512)*512)));
            return $data;
        } else return true;
    }
    
    
    
    // Return filelist of entire tar file
    function filelist() {
        if ($this->traversed) return $this->filelist;
        if (!$this->reset()) return array(); // Empty file?
        while ($this->next());
        return $this->filelist;
    }
    
    
    
    // Trigger class error
    function nofile() {
        trigger_error('No file loaded in class_tar',E_USER_WARNING);
        return false;
    }
    
}

?>